﻿using System.Security.Cryptography;
using System.Text;

namespace ECommon
{
    /// <summary>
    /// 加密工具
    /// </summary>
    public static class EncryptTool
    {
        /// <summary>
        /// 通用加密密钥
        /// </summary>
        public const string GENERALKEY = "easykey!#%&(24680";

        /// <summary>
        /// aes解密
        /// </summary>
        /// <param name="key">解密密钥</param>
        /// <param name="base64EncryptStr">base64编码的密文</param>
        /// <returns>utf8编码的明文</returns>
        /// <exception cref="ArgumentNullException">变量为空异常</exception>
        public static string AESDecrypt(string key, string base64EncryptStr)
        {
            if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(base64EncryptStr))
            {
                throw new ArgumentNullException("key或者base64EncryptStr", "密钥或者密文为空");
            }
            byte[] buffer = Convert.FromBase64String(base64EncryptStr);
            using (Aes aes = CreateAes(key))
            {
                ICryptoTransform cTransform = aes.CreateDecryptor();
                byte[] bytes = cTransform.TransformFinalBlock(buffer, 0, buffer.Length);
                return Encoding.UTF8.GetString(bytes);
            }
        }

        /// <summary>
        /// aes加密
        /// </summary>
        /// <param name="key">加密密钥</param>
        /// <param name="utf8SourceStr">utf8编码的明文</param>
        /// <returns>base64编码的密文</returns>
        /// <exception cref="ArgumentNullException">变量为空异常</exception>
        public static string AESEncrypt(string key, string utf8SourceStr)
        {
            if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(utf8SourceStr))
            {
                throw new ArgumentNullException("key或者utf8SourceStr", "密钥或者明文为空");
            }
            byte[] bytes = Encoding.UTF8.GetBytes(utf8SourceStr);
            using (Aes aes = CreateAes(key))
            {
                ICryptoTransform cTransform = aes.CreateEncryptor();
                byte[] buffer = cTransform.TransformFinalBlock(bytes, 0, bytes.Length);
                return Convert.ToBase64String(buffer);
            }
        }

        /// <summary>
        /// 根据指定长度创建随机加密密钥
        /// </summary>
        /// <param name="length">指定长度</param>
        /// <returns>随机加密密钥</returns>
        /// <exception cref="ArgumentException">参数异常</exception>
        public static string CreateKey(int length)
        {
            if (length <= 0)
            {
                throw new ArgumentException("密钥指定长度必须大于0", "length");
            }
            Random rand = new Random();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < length; i++)
            {
                int index = rand.Next(0, CharString.Length);
                sb.Append(CharString[index]);
            }
            return sb.ToString();
        }

        /// <summary>
        /// 创建rsa密钥对
        /// </summary>
        /// <param name="privateKey">外传参数，私钥</param>
        /// <returns>公钥</returns>
        public static string CreateRsaKey(out string privateKey)
        {
            privateKey = "";
            string res = "";
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048))
            {

                privateKey = rsa.ToXmlString(true);
                res = rsa.ToXmlString(false);
            }
            return res;
        }

        /// <summary>
        /// des解密
        /// </summary>
        /// <param name="key">解密密钥</param>
        /// <param name="base64EncryptStr">base64编码的密文</param>
        /// <returns>utf8编码的明文</returns>
        /// <exception cref="ArgumentNullException">变量为空异常</exception>
        public static string DesDecrypt(string key, string base64EncryptStr)
        {
            if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(base64EncryptStr))
            {
                throw new ArgumentNullException("key或者base64EncryptStr", "密钥或者密文为空");
            }
            using (DES des = CreateDes(key))
            {
                byte[] inputByteArray = Convert.FromBase64String(base64EncryptStr);
                using (MemoryStream mStream = new MemoryStream())
                {
                    CryptoStream cStream = new CryptoStream(mStream, des.CreateDecryptor(), CryptoStreamMode.Write);
                    cStream.Write(inputByteArray, 0, inputByteArray.Length);
                    cStream.FlushFinalBlock();
                    cStream.Clear();
                    cStream.Close();
                    return Encoding.UTF8.GetString(mStream.ToArray());
                }
            }
        }

        /// <summary>
        /// des加密
        /// </summary>
        /// <param name="key">加密密钥</param>
        /// <param name="utf8SourceStr">utf8编码的明文</param>
        /// <returns>base64编码的密文</returns>
        /// <exception cref="ArgumentNullException">参数错误</exception>
        public static string DesEncrypt(string key, string utf8SourceStr)
        {
            if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(utf8SourceStr))
            {
                throw new ArgumentNullException("key或者utf8SourceStr", "密钥或者明文为空");
            }
            using (DES des = CreateDes(key))
            {
                byte[] inputByteArray = Encoding.UTF8.GetBytes(utf8SourceStr);
                using (MemoryStream mStream = new MemoryStream())
                {
                    CryptoStream cStream = new CryptoStream(mStream, des.CreateEncryptor(), CryptoStreamMode.Write);
                    cStream.Write(inputByteArray, 0, inputByteArray.Length);
                    cStream.FlushFinalBlock();
                    cStream.Clear();
                    cStream.Close();
                    return Convert.ToBase64String(mStream.ToArray());
                }
            }
        }

        /// <summary>
        /// 创建rsa签名
        /// </summary>
        /// <param name="privateKey">私钥</param>
        /// <param name="utf8SourceStr">utf8编码的待签名字符串</param>
        /// <returns>签名</returns>
        /// <exception cref="ArgumentNullException">变量为空异常</exception>
        public static string RsaCreateSign(string privateKey, string utf8SourceStr)
        {
            if (string.IsNullOrWhiteSpace(privateKey) || string.IsNullOrWhiteSpace(utf8SourceStr))
            {
                throw new ArgumentNullException("privateKey或者utf8SourceStr", "私钥或者要签名的字符串为空");
            }
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048))
            {
                rsa.FromXmlString(privateKey);
                byte[] bytes = Encoding.UTF8.GetBytes(utf8SourceStr);
                byte[] buffer = rsa.SignData(bytes, SHA1.Create());
                return Convert.ToBase64String(buffer);
            }
        }

        /// <summary>
        /// rsa验签
        /// </summary>
        /// <param name="publicKey">公钥</param>
        /// <param name="utf8SourceStr">utf8编码的待验签字符串</param>
        /// <param name="signature">签名</param>
        /// <returns>验签结果</returns>
        /// <exception cref="ArgumentNullException">变量为空异常</exception>
        public static bool RsaValidSign(string publicKey, string utf8SourceStr, string signature)
        {
            if (string.IsNullOrWhiteSpace(publicKey) || string.IsNullOrWhiteSpace(utf8SourceStr) || string.IsNullOrWhiteSpace(signature))
            {
                throw new ArgumentNullException("publicKey, utf8SourceStr, signature", "公钥、待验签的utf8编码字符串或者签名为空");
            }
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048))
            {
                rsa.FromXmlString(publicKey);
                byte[] signBuffer = Convert.FromBase64String(signature);
                byte[] buffer = Encoding.UTF8.GetBytes(utf8SourceStr);
                return rsa.VerifyData(buffer, SHA1.Create(), signBuffer);
            }
        }

        /// <summary>
        /// rsa解密
        /// </summary>
        /// <param name="privateKey">私钥</param>
        /// <param name="base64EncryptStr">base64编码的密文</param>
        /// <returns>utf8编码的明文</returns>
        /// <exception cref="ArgumentNullException">变量为空异常</exception>
        public static string RsaDecrypt(string privateKey, string base64EncryptStr)
        {
            if (string.IsNullOrWhiteSpace(privateKey) || string.IsNullOrWhiteSpace(base64EncryptStr))
            {
                throw new ArgumentNullException("privateKey或者base64EncryptStr", "私钥或者base64编码的密文为空");
            }
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048))
            {
                rsa.FromXmlString(privateKey);
                byte[] buffer = Convert.FromBase64String(base64EncryptStr);
                byte[] bytes = rsa.Decrypt(buffer, true);
                return Encoding.UTF8.GetString(bytes);
            }
        }

        /// <summary>
        /// ras加密
        /// </summary>
        /// <param name="publicKey">公钥</param>
        /// <param name="utf8SourceStr">utf8编码的明文</param>
        /// <returns>base64编码的密文</returns>
        /// <exception cref="ArgumentNullException">变量为空异常</exception>
        public static string RsaEncrypt(string publicKey, string utf8SourceStr)
        {
            if (string.IsNullOrWhiteSpace(publicKey) || string.IsNullOrWhiteSpace(utf8SourceStr))
            {
                throw new ArgumentNullException("publicKey或者utf8SourceStr", "公钥或者utf8编码的明文为空");
            }
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048))
            {
                rsa.FromXmlString(publicKey);
                byte[] bytes = Encoding.UTF8.GetBytes(utf8SourceStr);
                byte[] buffer = rsa.Encrypt(bytes, true);
                return Convert.ToBase64String(buffer);
            }
        }

        /// <summary>
        /// 字符串的三次对称解密
        /// </summary>
        /// <param name="key">解密密钥</param>
        /// <param name="base64EncryptStr">base64编码的密文</param>
        /// <returns>utf8编码的明文</returns>
        /// <exception cref="ArgumentNullException">变量为空异常</exception>
        public static string TripleDESDecrypt(string key, string base64EncryptStr)
        {
            if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(base64EncryptStr))
            {
                throw new ArgumentNullException("key or base64EncryptStr", "key or source string is null");
            }
            using (TripleDES triDes = CreateTripleDES(key))
            {
                byte[] inputByteArray = Convert.FromBase64String(base64EncryptStr);
                using (MemoryStream mStream = new MemoryStream())
                {
                    CryptoStream cStream = new CryptoStream(mStream, triDes.CreateDecryptor(), CryptoStreamMode.Write);
                    cStream.Write(inputByteArray, 0, inputByteArray.Length);
                    cStream.FlushFinalBlock();
                    cStream.Clear();
                    cStream.Close();
                    return Encoding.UTF8.GetString(mStream.ToArray());
                }
            }
        }

        /// <summary>
        /// 文件的三次对称解密
        /// </summary>
        /// <param name="key">解密密钥</param>
        /// <param name="encryptFilePath">密文文件路径</param>
        /// <param name="decryptFilePath">明文文件路径</param>
        /// <exception cref="ArgumentNullException">变量为空异常</exception>
        /// <exception cref="ArgumentException">参数错误异常</exception>
        public static bool TripleDESDecryptFile(string key, string encryptFilePath, string decryptFilePath)
        {
            if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(decryptFilePath) || string.IsNullOrWhiteSpace(encryptFilePath))
            {
                throw new ArgumentNullException("key，decryptFilePath，encryptFilePath", "解密密钥、明文或密文文件路径为空");
            }
            decryptFilePath = decryptFilePath.Replace('\\', Path.DirectorySeparatorChar);
            encryptFilePath= encryptFilePath.Replace('\\', Path.DirectorySeparatorChar);
            if (!File.Exists(decryptFilePath) || !Directory.Exists(Path.GetDirectoryName(encryptFilePath)))
            {
                throw new ArgumentException("明文或密文文件或路径不存在");
            }
            using (TripleDES triDes = CreateTripleDES(key))
            {
                using (FileStream fWrite = new FileStream(decryptFilePath, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    CryptoStream cStream = new CryptoStream(fWrite, triDes.CreateDecryptor(), CryptoStreamMode.Write);
                    FileStream fRead = new FileStream(encryptFilePath, FileMode.Open, FileAccess.Read);
                    int length = 0;
                    byte[] buffer = new byte[1024 * 1024];
                    do
                    {
                        length = fRead.Read(buffer, 0, 1024 * 1024);
                        cStream.Write(buffer, 0, length);
                        cStream.Flush();
                        cStream.Clear();
                    } while (length == 1024 * 1024);
                    //cStream.FlushFinalBlock();
                    cStream.Clear();
                    cStream.Close();
                }
            }
            return true;
        }

        /// <summary>
        /// 将密文通过三次对称解密为文件
        /// </summary>
        /// <param name="key">解密密钥</param>
        /// <param name="base64EncryptStr">base64编码的密文</param>
        /// <param name="decryptFilePath">密文文件路径</param>
        /// <returns>解密结果</returns>
        /// <exception cref="ArgumentNullException">变量为空异常</exception>
        ///// <exception cref="ArgumentException">变量错误异常</exception>
        public static bool TripleDESDecryptStringToFile(string key, string base64EncryptStr, string decryptFilePath)
        {
            if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(decryptFilePath) || string.IsNullOrWhiteSpace(base64EncryptStr))
            {
                throw new ArgumentNullException("key，base64EncryptStr或者decryptFilePath", "解密密钥、base64编码的密文或者解密文件路径为空");
            }
            decryptFilePath = decryptFilePath.Replace('\\', Path.DirectorySeparatorChar);
            //if (!File.Exists(decryptFilePath))
            //{
            //    throw new ArgumentException("path not exist before triple des decrypt");
            //}
            MemoryStream ms = new MemoryStream(Convert.FromBase64String(base64EncryptStr));
            using (TripleDES triDes = CreateTripleDES(key))
            {
                using (FileStream fWrite = new FileStream(decryptFilePath, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    CryptoStream cStream = new CryptoStream(fWrite, triDes.CreateDecryptor(), CryptoStreamMode.Write);
                    int length = 0;
                    byte[] buffer = new byte[1024 * 1024];
                    do
                    {
                        length = ms.Read(buffer, 0, 1024 * 1024);
                        cStream.Write(buffer, 0, length);
                        cStream.Flush();
                        cStream.Clear();
                    } while (length == 1024 * 1024);
                    //cStream.FlushFinalBlock();
                    cStream.Clear();
                    cStream.Close();
                }
            }
            ms.Close();
            ms.Dispose();
            return true;
        }

        /// <summary>
        /// 字符串三次对称加密
        /// </summary>
        /// <param name="key">加密密钥</param>
        /// <param name="utf8SourceStr">utf8编码的明文</param>
        /// <returns>base64编码的密文</returns>
        /// <exception cref="ArgumentNullException">变量为空异常</exception>
        public static string TripleDESEncrypt(string key, string utf8SourceStr)
        {
            if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(utf8SourceStr))
            {
                throw new ArgumentNullException("key或者utf8SourceStr", "加密密钥或者utf8编码的明文为空");
            }
            using (TripleDES triDes = CreateTripleDES(key))
            {
                byte[] inputByteArray = Encoding.UTF8.GetBytes(utf8SourceStr);
                using (MemoryStream mStream = new MemoryStream())
                {
                    CryptoStream cStream = new CryptoStream(mStream, triDes.CreateEncryptor(), CryptoStreamMode.Write);
                    cStream.Write(inputByteArray, 0, inputByteArray.Length);
                    cStream.FlushFinalBlock();
                    cStream.Clear();
                    cStream.Close();
                    return Convert.ToBase64String(mStream.ToArray());
                }
            }
        }

        /// <summary>
        /// 文件的三次对称加密
        /// </summary>
        /// <param name="key">加密密钥</param>
        /// <param name="decryptFilePath">明文文件路径</param>
        /// <param name="encryptFilePath">密文文件路径</param>
        /// <exception cref="ArgumentNullException">变量为空异常</exception>
        /// <exception cref="ArgumentException">参数错误异常</exception>
        public static bool TripleDESEncryptFile(string key, string decryptFilePath, string encryptFilePath)
        {
            if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(decryptFilePath) || string.IsNullOrWhiteSpace(encryptFilePath))
            {
                throw new ArgumentNullException("key，decryptFilePath或者encryptFilePath", "加密密钥、明文或密文文件路径为空");
            }
            decryptFilePath = decryptFilePath.Replace('\\', Path.DirectorySeparatorChar);
            encryptFilePath = encryptFilePath.Replace('\\', Path.DirectorySeparatorChar);
            if (!File.Exists(decryptFilePath) || !Directory.Exists(Path.GetDirectoryName(encryptFilePath)))
            {
                throw new ArgumentException("明文或密文文件路径或目录不存在");
            }
            using (TripleDES triDes = CreateTripleDES(key))
            {
                using (FileStream fWrite = new FileStream(encryptFilePath, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    CryptoStream cStream = new CryptoStream(fWrite, triDes.CreateEncryptor(), CryptoStreamMode.Write);
                    FileStream fRead = new FileStream(decryptFilePath, FileMode.Open, FileAccess.Read);
                    int length = 0;
                    byte[] buffer = new byte[1024 * 1024];
                    do
                    {
                        length = fRead.Read(buffer, 0, 1024 * 1024);
                        cStream.Write(buffer, 0, length);
                        cStream.Flush();
                        cStream.Clear();
                    } while (length == 1024 * 1024);
                    //cStream.FlushFinalBlock();
                    cStream.Clear();
                    cStream.Close();
                }
            }
            return true;
        }

        /// <summary>
        /// 将文件通过三次对称加密为base64编码的字符串
        /// </summary>
        /// <param name="key">加密密钥</param>
        /// <param name="decryptFilePath">明文文件路径</param>
        /// <returns>base64编码的密文</returns>
        /// <exception cref="ArgumentNullException">变量为空异常</exception>
        /// <exception cref="ArgumentException">参数错误异常</exception>
        public static string TripleDESEncryptFileToString(string key, string decryptFilePath)
        {
            if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(decryptFilePath))
            {
                throw new ArgumentNullException("key or decryptFilePath", "密钥或明文文件路径为空");
            }
            decryptFilePath = decryptFilePath.Replace('\\', Path.DirectorySeparatorChar);
            if (!File.Exists(decryptFilePath))
            {
                throw new ArgumentException("明文文件不存在");
            }
            MemoryStream mStream = new MemoryStream();
            using (TripleDES triDes = CreateTripleDES(key))
            {
                CryptoStream cStream = new CryptoStream(mStream, triDes.CreateEncryptor(), CryptoStreamMode.Write);
                FileStream fRead = new FileStream(decryptFilePath, FileMode.Open, FileAccess.Read);
                int length = 0;
                byte[] buffer = new byte[1024 * 1024];
                do
                {
                    length = fRead.Read(buffer, 0, 1024 * 1024);
                    cStream.Write(buffer, 0, length);
                    cStream.Flush();
                    cStream.Clear();
                } while (length == 1024 * 1024);
                //cStream.FlushFinalBlock();
                cStream.Clear();
                cStream.Close();
            }
            string res = Convert.ToBase64String(mStream.ToArray());
            mStream.Close();
            mStream.Dispose();
            return res;
        }

        /// <summary>
        /// 加密基础字符
        /// </summary>
        private const string CharString = "abcdefghijklmnopqrstuvwxyz0192837465ZYXWVUTSRQPONMLKJIHGFEDCBA";

        /// <summary>
        /// 创建des加密对象
        /// </summary>
        /// <param name="key">加密密钥</param>
        /// <returns>des加密对象</returns>
        private static DES CreateDes(string key)
        {
            string hashKey = Md5Tool.GetMd5ByString(key);
            string iv = hashKey.Substring(hashKey.Length - 8, 8);
            string encryptKey = hashKey.Substring(0, 8);
            byte[] byteIv = Encoding.UTF8.GetBytes(iv);
            byte[] byteKey = Encoding.UTF8.GetBytes(encryptKey);

            DES provider = DES.Create();
            provider.Key = byteKey;
            provider.IV = byteIv;
            return provider;
        }

        /// <summary>
        /// 创建aes加密对象
        /// </summary>
        /// <param name="key">加密密钥</param>
        /// <returns>aes加密对象</returns>
        private static Aes CreateAes(string key)
        {
            string hashKey = Md5Tool.GetMd5ByString(key);
            string iv = hashKey.Substring(hashKey.Length - 16, 16);
            byte[] byteIv = Encoding.UTF8.GetBytes(iv);
            byte[] byteKey = Encoding.UTF8.GetBytes(hashKey);
            Aes aes = Aes.Create();
            aes.Key = byteKey;
            aes.IV = byteIv;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;
            return aes;
        }

        /// <summary>
        /// 创建三次非对称加密对象
        /// </summary>
        /// <param name="key">加密密钥</param>
        /// <returns>三次非对称加密对象</returns>
        private static TripleDES CreateTripleDES(string key)
        {
            string hashKey = Md5Tool.GetMd5ByString(key);
            string iv = hashKey.Substring(hashKey.Length - 8, 8);
            byte[] byteIv = Encoding.UTF8.GetBytes(iv);
            byte[] byteKey = Encoding.UTF8.GetBytes(hashKey.Substring(0, 24));
            TripleDES tripleDES = TripleDES.Create();
            tripleDES.Key = byteKey;
            tripleDES.IV = byteIv;
            tripleDES.Mode = CipherMode.ECB;
            tripleDES.Padding = PaddingMode.PKCS7;
            return tripleDES;
        }
    }
}